package edu.uj.petri.model;

import edu.uj.util.Observed;
import edu.uj.util.ObserverList;
import edu.uj.petri.commands.Memento;
import edu.uj.util.Observer;
import java.util.Iterator;

/**
 * @author Piotrek
 */
public class Transition extends Node implements Observed {
    private TransitionImpl impl;
    
    /** event code for Observers */
    public static final int ACTIVATION_CHANGE = 7102;
    
    /** event code for Observers */
    public static final int NAME_CHANGE = 7103;
    
    private ObserverList observerList;
    
    
    Transition(TransitionImpl impl) {
        this.impl = impl;
        observerList = new ObserverList();
    }
    
    /**
     * activates transition (transition may change colour). Check canBeFired() first.
     * Does not move any tokens. Use {@link #fire} method to run this transition.
     */
    public void activate() {
        impl.activate();
        observerList.notifyAllSubscribers(ACTIVATION_CHANGE);
    }
    
    /**
     * deactivates transition. Does not move any tokens.
     */
    public void deactivate() {
        impl.deactivate();
        observerList.notifyAllSubscribers(ACTIVATION_CHANGE);
    }
    
    /**
     * checks if transition has been marked as active; <strong>warning:</strong>
     * this does not mean that the transition can be fired! Use {@link #canBeFired()}
     * method instead.
     */
    public boolean isActive() {
        return impl.isActive();
    }
    
    /**
     * returns true if transition can be fired.
     */
    public boolean canBeFired() {
        Iterator<Arc> iter=impl.getIncomingArcsIterator();
        while(iter.hasNext()) {
            if (! iter.next().canBeFired())
                return false;
        }
        return true;
    }
    
    /**
     * fires transition, moves tokens.
     */
    public void fire() {
        Iterator<Arc> incoming=impl.getIncomingArcsIterator();
        Iterator<Arc> outgoing=impl.getOutgoingArcsIterator();
        
        while (incoming.hasNext()) {
            incoming.next().fire();
        }
        
        while (outgoing.hasNext()) {
            outgoing.next().fire();
        }
    }
    
    public void addIncomingArc(Arc arc) {
        impl.addIncomingArc(arc);
    }
    
    public void addOutgoingArc(Arc arc) {
        impl.addOutgoingArc(arc);
    }
    
    public void removeIncomingArc(Arc arc) {
        impl.removeIncomingArc(arc);
    }
    
    public void removeOutgoingArc(Arc arc) {
        impl.removeOutgoingArc(arc);
    }
    
    /**
     * returns arc from this transition to given place or null if there is no
     * such arc. Arcs from place to transition do not count.
     */
    public Arc getArcTo(Place place) {
        return findArcOn(impl.getOutgoingArcsIterator(), place);
    }
    
    /**
     * returns arc from given place to this transition or null if there is no
     * such arc. Arc from transition to place do not count.
     */
    public Arc getArcFrom(Place place) {
        return findArcOn(impl.getIncomingArcsIterator(), place);
    }
    
    /**
     * finds place on collection of arcs adjacent to this transition given
     * by an iterator. returns null when not found.
     */
    private Arc findArcOn(Iterator<Arc> iter, Place place) {
        while (iter.hasNext()) {
            Arc arc = iter.next();
            if (place.equals(arc.getAnotherEnd(this)))
                return arc;
        }
        return null;
    }
    
    public void setName(String name) {
        impl.setName(name);
        observerList.notifyAllSubscribers(NAME_CHANGE);
    }
    
    public String getName() {
        return impl.getName();
    }

    public String toString() {
        return getName();
    }
    

    
    /** stores internal state in a memento */
    public Memento getMemento() {
        return impl.getMemento();
    }
    
    /**
     * restores internal state of object to state stored in the memento
     */
    public void applyMemento(Memento memento) {
        impl.applyMemento(memento);
        observerList.notifyAllSubscribers(ACTIVATION_CHANGE);
        observerList.notifyAllSubscribers(NAME_CHANGE);
    }
    
    public void subscribe(Observer o) {
        observerList.subscribe(o);
    }
    
    public void unsubscribe(Observer o) {
        observerList.unsubscribe(o);
    }
    
    private static final long serialVersionUID = -6218041651474157490L;
}
